import java.util.ArrayList;


public class Environment {

	int size;
	double distribution = 50;
	double mortality = 20;
	static int coopCost;
	static int defCost;
	int reward;

	ArrayList<Agent> agentList = new ArrayList<Agent>();


	public Environment(int size, double distribution, double mortality, int coopCost, int defCost, int reward){
		//set variables
		this.reward = reward;
		this.size = size;
		this.distribution = distribution;
		this.mortality = mortality;
		this.coopCost =  coopCost;
		this.defCost = defCost;

		inizialize();
	}

	public void doGeneration(int playerAmount){
		
		//interaction
		this.interact(playerAmount);

		// Mortality.
		this.mortality();

		// Reproduction.
		this.reproduction();
	}
	
	public void interact(double n){
		double num = agentList.size() / n;
		int win = 0;
		for (int u = 0; u < num ; u++){
			ArrayList<Agent> l = new ArrayList<Agent>();

			for(int i=0;i<n;i++){
				Agent a = (Agent) getRandomAgent();
				l.add(a);
			}
			for (Agent a : l)
				a.play(l);
			
			for(Agent a : l){
				if(a.getType() == 1)
					win++;
			}
			double winRatio = n/2;
			if(win > winRatio){	
				reward(l);
			}
			else{
				for(int i=0; i<l.size(); i++)
					l.get(i).setLastGameWin(false);
			}
			
			win = 0;
			
			for(int i=0; i<l.size(); i++)
				l.get(i).setLastGame(l.get(i).getType());
		}
	}

	public void reward(ArrayList<Agent> l)
	{
			// Reward all.
				for(Agent p : l){
					p.reward(reward);
					p.setLastGameWin(true);
				}
	}

	public void mortality(){
		int size = agentList.size();
		int num = (int) (size * mortality);
		
		for(int i = 0; i<num ; i++){
			agentList.remove((int) ((size * Math.random())) );
			size--;
		}
	}

	public void reproduction(){
		int missing = size - agentList.size();

		//calculate averages
		double coopFitness=0;
		double defFitness=0;
		int coopNum=0;
		int defNum=0;

		for(Agent a : agentList){
			Agent p = (Agent) a;

			if(p.getType() == 1){
				coopNum++;
				coopFitness += p.getFitness();
			}
			else{
				defNum++;
				defFitness += p.getFitness();
			}
		}

		double avgDef = defFitness/(double) defNum;
		double avgCoop = coopFitness/(double) coopNum;
		double size = (double) agentList.size();

		double defStuff = avgDef * (defNum / size);
		double coopStuff = avgCoop * (coopNum / size);

		double fecondity = coopStuff / (coopStuff + defStuff);

		//System.out.println("fecondity: "+ fecondity + " cooperators: " + coopNum + " defectors: " + defNum);
		//System.out.println(defNum);

		for (int i = 0 ; i<missing ; i++){
				// Inserts Agent in relation to the above calculation.
				if(Math.random() <= fecondity)
					agentList.add(new Agent(1,coopCost));
				else
					agentList.add(new Agent(0,defCost));
		}

		// Resets fitness for next generation.
		if(missing != 0){
			for (Agent a : agentList){
				Agent p = (Agent) a;
				p.setFitness(0);
			}
		}
	}

	// Get a random Agent.
	public Agent getRandomAgent(){

		return agentList.get((int) (agentList.size() * Math.random()));
	}
	
	// Fill the Agent List.
	private void inizialize(){
		
		int tempDiv = (int) (this.size * Math.round(this.distribution))/100;
		
		// Constructing Agent objects, with different attributes, and adding them to the List.
		for(int i = 0 ; i < tempDiv ; i++)
			agentList.add(new Agent(1,coopCost));
		
		for(int i = tempDiv ; i < (this.size) ; i++)
			agentList.add(new Agent(0,defCost));
	}

	// Data collection.
	public void print(){
		int coopNum = 0;
		int defNum = 0;
		double coopFitness = 0;
		double defFitness = 0;
		
		for(Agent a : agentList){
			Agent p = (Agent) a;

			if(p.getType() == 1){
				coopFitness += p.getFitness();
				coopNum++;
			}
			else{
				defFitness += p.getFitness();
				defNum++;
			}
		}

		int size = agentList.size();
		
		// Divides the additions to obtain average value.
		double avgCoopFitness = coopFitness/coopNum, avgDefFitness = defFitness/defNum;
		
		// Prints average values to console.
		System.out.println("Size of the list: " + size);
		System.out.println("Average cooperater fitness: " + avgCoopFitness + ", calculated for " + coopNum + " cooperating Agents.");
		System.out.println("Average defector fitness: " + avgDefFitness + ", calculated for " + defNum + " defecting Agents.\n");
	}
	
	public void printToData(){
		int coopNum = 0;
		int defNum = 0;
		
		for(Agent a : agentList){

			if(a.getType() == 1)
				coopNum++;
			else
				defNum++;
		}

		double size = agentList.size();
		
		double devNum = 100;
		if(defNum != 0){
			devNum = (100*coopNum)/size;
		}
		
		System.out.println((int)devNum);
	}
}
